package estruturas;

import java.util.ArrayList;
import java.util.Iterator;


public class HashTable<K extends Comparable<? super K>, V> implements Iterator<Node<K, V>>{

        private int size;
        private int numberOfElements;
        private ArrayList<Node<K, V>>[] buckets;
        private int current; //for iterator
        private int currentBucket; //for iterator
        private int currentBucketPosition; //for iterator


        public HashTable(int size) {
                super();
                this.size = size;
                this.initialize();
        }
        
        public HashTable(int size, ArrayList<Node<K, V>> nodes) {
                super();
                this.size = size;
                this.initialize();
                this.addAll(nodes);
        }

        public HashTable() {
                super();
                this.size = 701;
                this.initialize();
        }
        
        public HashTable(ArrayList<Node<K, V>> nodes) {
                super();
                this.size = 701;
                this.initialize();
                this.addAll(nodes);
        }

        @SuppressWarnings("unchecked")
        private void initialize() {
                this.numberOfElements = 0;
                setDefaultIterator();
                buckets = new ArrayList[this.size];
        }

        public int getSize() {
                return size;
        }

        public ArrayList<Node<K, V>> getAll() {
                
                ArrayList<Node<K, V>> nodes = new ArrayList<Node<K, V>>();
                
                for (int i = 0; i < buckets.length; i++) {
                        if (buckets[i] != null)
                                nodes.addAll(buckets[i]);
                }
                return nodes;
        }

        public int[] getAllCodes() {
                int[] codes = new int[this.numberOfElements];
                int k = 0;
                for (int i = 0; i < buckets.length; i++) {
                        if (buckets[i] != null)
                                for (int j = 0; j < buckets[i].size(); j++) {
                                        codes[k] = buckets[i].get(j).hashCode();
                                        k++;
                                }
                }
                return codes;
        }

        private int hashCode(K k) {
                int pos = Math.abs(k.hashCode()) % size;
                
                return reHashing(k, pos);
        }
        
        private int reHashing(K k, int pos){
                
                int counter = 0;

                while ((counter < size) && (this.buckets[pos] != null) && (this.buckets[pos].get(0).getKey().compareTo(k) != 0)){
                        counter++;
                        pos = (pos + 1) % size;
                }
                
                return (counter == this.size ? -1 : pos); //-1 erro
        }
        
        public void addAll(ArrayList<Node<K, V>> nodes) {
                
                for (Node<K, V> node: nodes) {
                        this.add(node);
                }
        }
        
        
        public void add(Node<K, V> node) {
                

                //if (this.get(node.getKey()) == null) {
                
                        int pos = this.hashCode(node.getKey());
                        
                        try {
                                buckets[pos].add(node);
                                
                        } catch (NullPointerException e) {
                                
                                buckets[pos] = new ArrayList<Node<K, V>>();
                                buckets[pos].add(node);
                        }finally{       
                                this.numberOfElements++;
                        }
                
        }

        public V get(K k) {
                
                int pos = this.hashCode(k);
                
                if (buckets[pos] == null)
                        return null;
                                
                for (int i = 0; i < buckets[pos].size(); i++) {
                                                
                        if (buckets[pos].get(i).getKey().equals(k))
                                return buckets[pos].get(i).getValue();
                }
                return null;
        }
        
        public ArrayList<Node<K, V>> getCollision(K k) {
                
                int pos = this.hashCode(k);
                
                if (buckets[pos] == null)
                        return null;
                
                return buckets[pos];
        }

        public void remove(K k) {
                V v = this.get(k);
                int pos = this.hashCode(k);
                this.buckets[pos].remove(v);
        }

        public boolean hasNext() {
                // TODO Auto-generated method stub
                return (this.current < (this.numberOfElements-1));
        }

        public Node<K, V> next() {
                // TODO Auto-generated method stub
                Node<K, V> node = null;
                
                current++;
                currentBucketPosition++;
                
                try {
                        node = this.buckets[this.currentBucket].get(currentBucketPosition);
                }
                catch(Exception e){
                        
                        do {
                                this.currentBucket++;
                        } while ((this.currentBucket < buckets.length) && (this.buckets[this.currentBucket] == null));

                        this.currentBucketPosition = 0;
                        node = this.buckets[this.currentBucket].get(this.currentBucketPosition);
                        
                        return node;
                }
                
                return node;
        }

        public void remove() {
                // TODO Auto-generated method stub
                
        }
        
        public void setDefaultIterator() {
                this.current = -1;
                this.currentBucket = 0;
                this.currentBucketPosition = -1;                
        }

}